﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Reborn_Server.App1AppHandler;
using System.Timers;
using Reborn_WorldServer;

namespace App1AppHandler
{
    public class LootManager
    {
        public volatile Dictionary<string, FieldLootItems> MapItemsLoot = new Dictionary<string,FieldLootItems>();
        private Map ParentMap;

        public LootManager(Map parentMap)
        {
            ParentMap = parentMap;
        }

        public bool TakeCoins(int MapX, int MapY, int numCoins)
        {
            Coins MyCoins = new Coins(ParentMap.GameHandler);

            FieldLootItems MitemToFind;

            lock (MapItemsLoot)
            {
                if (!(MapItemsLoot.TryGetValue(MapX.ToString() + "," + MapY.ToString(), out MitemToFind)))
                    return false;

                bool foundItem = false;
                foreach (Loot loot in MitemToFind.LootItems)
                {
                    if (loot.ItemType == "Coins")
                    {
                        foundItem = true;
                        MyCoins = (Coins)loot;
                        if (MyCoins.NumCoins < numCoins)
                            return false;

                        break;
                    }
                }
                if (foundItem == true)
                {
                    MitemToFind.LootItems.Remove(MyCoins);
                    MyCoins.timerDestroyStop();
                    if (MyCoins.NumCoins - numCoins > 0)
                    {
                        Coins newCoins = new Coins(ParentMap.GameHandler);
                        newCoins.GameUID = ParentMap.GameHandler.ItemIDGen.GetUID();
                        newCoins.Bag = MyCoins.Bag;
                        newCoins.CanConsume = MyCoins.CanConsume;
                        newCoins.ClassName = MyCoins.ClassName;
                        newCoins.Description = MyCoins.Description;
                        newCoins.ID = MyCoins.ID;
                        newCoins.ImageURL = MyCoins.ImageURL;
                        newCoins.ItemType = MyCoins.ItemType;
                        newCoins.NumCoins = MyCoins.NumCoins - numCoins;
                        newCoins.Name = newCoins.NumCoins.ToString() + " Coins";
                        newCoins.timerDestroyStart(ParentMap.MapID, ParentMap.MapInstance, MitemToFind.MapX, MitemToFind.MapY, false);
                        MitemToFind.LootItems.Add(newCoins);
                    }
                }
                else
                    return false;
            }

            MapItem MapItemToUpdate = null;
            bool UpdateMapItem = false;
            List<Character> CharsToUpdate = new List<Character>();
            List<Character> CharsToUpdate_Loot = new List<Character>();

            LockItem lockItem = ParentMap.GameHandler.GetLockItem(ParentMap.MapID, ParentMap.MapInstance);
            lock (lockItem.LockObj)
            {
                foreach (MapItem mapitem in ParentMap.MapItems)
                {
                    if (mapitem.MapX == MapX && mapitem.MapY == MapY)
                    {
                        if (MitemToFind.LootItems.Count == 0 && mapitem.HasLoot == true)
                        {
                            UpdateMapItem = true;
                            mapitem.HasLoot = false;
                            MapItemToUpdate = mapitem;
                        }
                    }
                    if ((mapitem.MapX - MapX >= -1 && mapitem.MapX - MapX <= 1) && (mapitem.MapY - MapY >= -1 && mapitem.MapY - MapY <= 1))
                    {
                        foreach (CreatureBase creatureBase in mapitem.CreaturesBases)
                        {
                            if (creatureBase is Character)
                            {
                                Character character = (Character)creatureBase;
                                CharsToUpdate_Loot.Add(character);
                            }
                        }
                    }
                    if ((mapitem.MapX - MapX >= -4 && mapitem.MapX - MapX <= 4) && (mapitem.MapY - MapY >= -4 && mapitem.MapY - MapY <= 4))
                    {
                        foreach (CreatureBase creatureBase in mapitem.CreaturesBases)
                        {
                            if (creatureBase is Character)
                            {
                                Character character = (Character)creatureBase;
                                CharsToUpdate.Add(character);
                            }
                        }
                    }
                }
                if (UpdateMapItem)
                {
                    foreach (Character character in CharsToUpdate)
                    {
                        character.AddMapItem(MapItemToUpdate);
                    }
                }
                foreach (Character character in CharsToUpdate_Loot)
                {
                    character.AddFieldLoot(MitemToFind);
                }
            
            }//End Lock

            return true;
        }

        public void DropCoins(int MapX, int MapY, int numCoins, bool useExtendedGarbageTimer)
        {
            Coins MyCoins = new Coins(ParentMap.GameHandler);

            FieldLootItems MitemToFind;

            lock (MapItemsLoot)
            {
                if (!(MapItemsLoot.TryGetValue(MapX.ToString() + "," + MapY.ToString(), out MitemToFind)))
                {
                    MitemToFind = new FieldLootItems();
                    MitemToFind.MapX = MapX;
                    MitemToFind.MapY = MapY;
                    MapItemsLoot.Add(MapX.ToString() + "," + MapY.ToString(), MitemToFind);
                }

                bool foundItem = false;
                foreach (Loot loot in MitemToFind.LootItems)
                {
                    if (loot.ItemType == "Coins")
                    {
                        foundItem = true;
                        MyCoins = (Coins)loot;
                        break;
                    }
                }

                Coins newCoins = new Coins(ParentMap.GameHandler);
                newCoins.NumCoins = numCoins;
                if (foundItem == true)
                {
                    MitemToFind.LootItems.Remove(MyCoins);
                    newCoins.NumCoins = numCoins + MyCoins.NumCoins;
                }
                newCoins.GameUID = ParentMap.GameHandler.ItemIDGen.GetUID();
                newCoins.Bag = true;
                newCoins.CanConsume = false;
                newCoins.ID = MyCoins.ID;
                newCoins.ImageURL = "Coin";
                newCoins.ItemType = "Coins";
                newCoins.Name = newCoins.NumCoins.ToString() + " Coins";

                newCoins.timerDestroyStart(ParentMap.MapID, ParentMap.MapInstance, MitemToFind.MapX, MitemToFind.MapY, useExtendedGarbageTimer);
                MitemToFind.LootItems.Add(newCoins);
            }

            MapItem MapItemToUpdate = null;
            bool UpdateMapItem = false;
            List<Character> CharsToUpdate = new List<Character>();
            List<Character> CharsToUpdate_Loot = new List<Character>();

            LockItem lockItem = ParentMap.GameHandler.GetLockItem(ParentMap.MapID, ParentMap.MapInstance);
            lock (lockItem.LockObj)
            {
                foreach (MapItem mapitem in ParentMap.MapItems)
                {
                    if (mapitem.MapX == MapX && mapitem.MapY == MapY)
                    {
                        if (MitemToFind.LootItems.Count > 0 && mapitem.HasLoot == false)
                        {
                            UpdateMapItem = true;
                            mapitem.HasLoot = true;
                            MapItemToUpdate = mapitem;
                        }
                    }
                    if ((mapitem.MapX - MapX >= -1 && mapitem.MapX - MapX <= 1) && (mapitem.MapY - MapY >= -1 && mapitem.MapY - MapY <= 1))
                    {
                        foreach (CreatureBase creatureBase in mapitem.CreaturesBases)
                        {
                            if (creatureBase is Character)
                            {
                                Character character = (Character)creatureBase;
                                CharsToUpdate_Loot.Add(character);
                            }
                        }
                    }
                    if ((mapitem.MapX - MapX >= -4 && mapitem.MapX - MapX <= 4) && (mapitem.MapY - MapY >= -4 && mapitem.MapY - MapY <= 4))
                    {
                        foreach (CreatureBase creatureBase in mapitem.CreaturesBases)
                        {
                            if (creatureBase is Character)
                            {
                                Character character = (Character)creatureBase;
                                CharsToUpdate.Add(character);
                            }
                        }
                    }
                }
                if (UpdateMapItem)
                {
                    foreach (Character character in CharsToUpdate)
                    {
                        character.AddMapItem(MapItemToUpdate);
                    }
                }
                foreach (Character character in CharsToUpdate_Loot)
                {
                    character.AddFieldLoot(MitemToFind);
                }
            
            }//End Lock

        }

        public CorpseCharacter GetFirstCharacterCorpse(int MapX, int MapY)
        {
            //Lock the loot for this map
            lock (MapItemsLoot)
            {
                FieldLootItems MitemToFind;
                if (!(MapItemsLoot.TryGetValue(MapX.ToString() + "," + MapY.ToString(), out MitemToFind)))
                    return null;

                foreach (Loot loot in MitemToFind.LootItems)
                {
                    if (loot is CorpseCharacter)
                        return (CorpseCharacter)loot;
                }
                return null;
            }
        }

        public int getNumCorpses(int MapX, int MapY)
        {
            //Lock the loot for this map
            lock (MapItemsLoot)
            {
                FieldLootItems MapItemLoot;
                if (!(MapItemsLoot.TryGetValue(MapX.ToString() + "," + MapY.ToString(), out MapItemLoot)))
                    return 0;

                int result = 0;

                foreach (Loot loot in MapItemLoot.LootItems)
                {
                    if (loot is Corpse)
                        result++;
                }
                return result;
            }
        }

        public Corpse GetFirstCorpse(int MapX, int MapY)
        {
            //Lock the loot for this map
            lock (MapItemsLoot)
            {
                FieldLootItems MapItemLoot;
                if (!(MapItemsLoot.TryGetValue(MapX.ToString() + "," + MapY.ToString(), out MapItemLoot)))
                    return null;
                
                foreach (Loot loot in MapItemLoot.LootItems)
                {
                    if (loot is Corpse)
                        return (Corpse)loot;
                }
                return null;
            }
        }

        public Loot TakeItem(int MapX, int MapY, long GameUID)
        {
            Loot MyItem = new Loot(ParentMap.GameHandler);

            FieldLootItems MapItemLoot;

            lock (MapItemsLoot)
            {
                if (!(MapItemsLoot.TryGetValue(MapX.ToString() + "," + MapY.ToString(), out MapItemLoot)))
                    return null;

                bool foundItem = false;
                lock (MapItemLoot)//Lock the loot on this hex so we can remove
                {
                    foreach (Loot loot in MapItemLoot.LootItems)
                    {
                        if (loot.GameUID == GameUID)
                        {
                            foundItem = true;
                            MyItem = loot;
                            break;
                        }
                    }
                    if (foundItem == true)
                    {
                        MapItemLoot.LootItems.Remove(MyItem);
                        MyItem.timerDestroyStop();

                        if (MyItem.isGroundSpawn == true)
                        {
                            Loot newGroundSpawn = Managers.GameHandler.CreateItem(MyItem.ID);

                            //This will automatically start a timer to drop a new item once instaciated
                            PendingGroundSpawn pendingSpawn = new PendingGroundSpawn(this, newGroundSpawn, MyItem.respawnMinutes, MapX, MapY);

                            //Unflag this so if it gets put back on the ground it doesnt create another ground spawn
                            MyItem.isGroundSpawn = false;
                            MyItem.respawnMinutes = 0;
                        }
                    }
                    else
                        return null;
                }
            }

            MapItem MapItemToUpdate = null;
            bool UpdateMapItem = false;
            List<Character> CharsToUpdate = new List<Character>();
            List<Character> CharsToUpdate_Loot = new List<Character>();

            LockItem lockItem = ParentMap.GameHandler.GetLockItem(ParentMap.MapID, ParentMap.MapInstance);
            lock (lockItem.LockObj)
            {
                foreach (MapItem mapitem in ParentMap.MapItems)
                {
                    if (mapitem.MapX == MapX && mapitem.MapY == MapY)
                    {
                        if (MapItemLoot.LootItems.Count == 0 && mapitem.HasLoot == true)
                        {
                            UpdateMapItem = true;
                            mapitem.HasLoot = false;
                            MapItemToUpdate = mapitem;
                        }
                    }
                    if ((mapitem.MapX - MapX >= -1 && mapitem.MapX - MapX <= 1) && (mapitem.MapY - MapY >= -1 && mapitem.MapY - MapY <= 1))
                    {
                        //We dont need to lock the whole map cause we only care about the individual hexes
                        //we are not calculation against any other hexes.
                        lock (mapitem.CreaturesBases)
                        {
                            foreach (CreatureBase creatureBase in mapitem.CreaturesBases)
                            {
                                if (creatureBase is Character)
                                {
                                    Character character = (Character)creatureBase;
                                    CharsToUpdate_Loot.Add(character);
                                }
                            }
                        }
                    }
                    if ((mapitem.MapX - MapX >= -4 && mapitem.MapX - MapX <= 4) && (mapitem.MapY - MapY >= -4 && mapitem.MapY - MapY <= 4))
                    {
                        //We dont need to lock the whole map cause we only care about the individual hexes
                        //we are not calculation against any other hexes.
                        lock (mapitem.CreaturesBases)
                        {
                            foreach (CreatureBase creatureBase in mapitem.CreaturesBases)
                            {
                                if (creatureBase is Character)
                                {
                                    Character character = (Character)creatureBase;
                                    CharsToUpdate.Add(character);
                                }
                            }
                        }
                    }
                }
                if (UpdateMapItem)
                {
                    foreach (Character character in CharsToUpdate)
                    {
                        character.AddMapItem(MapItemToUpdate);
                    }
                }
                foreach (Character character in CharsToUpdate_Loot)
                {
                    character.AddFieldLoot(MapItemLoot);
                }

            }//End Lock

            return MyItem;
        }
        public void DropItems(int MapX, int MapY, Loot ItemToDrop, bool useExtendedGarbageTimer)
        {
            List<Loot> items = new List<Loot>();
            items.Add(ItemToDrop);
            DropItems(MapX, MapY, items, useExtendedGarbageTimer);
        }
        public void DropItems(int MapX, int MapY, List<Loot> ItemsToDrop, bool useExtendedGarbageTimer)
        {
            FieldLootItems MapItemLoot;

            if (!(MapItemsLoot.TryGetValue(MapX.ToString() + "," + MapY.ToString(), out MapItemLoot)))
            {
                MapItemLoot = new FieldLootItems();
                MapItemLoot.MapX = MapX;
                MapItemLoot.MapY = MapY;
                MapItemsLoot.Add(MapX.ToString() + "," + MapY.ToString(), MapItemLoot);
            }
            lock (MapItemsLoot)
            {
                foreach (Loot lootItem in ItemsToDrop)
                {
                    if (lootItem.ItemType == "Coins")
                    {
                        //Check if coins exist on ground & remove them
                        bool foundCoins = false;
                        Coins coinsOnField = new Coins(ParentMap.GameHandler);
                        foreach (Loot lootitem in MapItemLoot.LootItems)
                        {
                            if (lootitem.ItemType == "Coins")
                            {
                                coinsOnField = (Coins)lootitem;
                                MapItemLoot.LootItems.Remove(lootitem);
                                foundCoins = true;
                                break;
                            }
                        }
                        //Add old coins to new coins
                        if (foundCoins == true)
                        {
                            Coins coinsToAdd = (Coins)lootItem;
                            coinsToAdd.NumCoins = coinsToAdd.NumCoins + coinsOnField.NumCoins;
                            coinsToAdd.Name = coinsToAdd.NumCoins.ToString() + " Coins";
                        }
                    }

                    //Ground spawns to not get cleaned up
                    if (lootItem.isGroundSpawn == false)
                        lootItem.timerDestroyStart(ParentMap.MapID, ParentMap.MapInstance, MapItemLoot.MapX, MapItemLoot.MapY, useExtendedGarbageTimer);
                        
                    //Add Loot To MapItem
                    MapItemLoot.LootItems.Add(lootItem);
                }
            }

            List<Character> CharsToUpdate = new List<Character>();
            List<Character> CharsToUpdate_Loot = new List<Character>();

            MapItem MapItemToUpdate = null;

            LockItem lockItem = ParentMap.GameHandler.GetLockItem(ParentMap.MapID, ParentMap.MapInstance);
            lock (lockItem.LockObj)
            {
                foreach (MapItem mapitem in ParentMap.MapItems)
                {
                    if (mapitem.MapX == MapX && mapitem.MapY == MapY)
                    {
                        if (mapitem.HasLoot == false)
                        {
                            mapitem.HasLoot = true;
                            MapItemToUpdate = mapitem;
                        }
                    }
                    if ((mapitem.MapX - MapX >= -4 && mapitem.MapX - MapX <= 4) && (mapitem.MapY - MapY >= -4 && mapitem.MapY - MapY <= 4))
                    {
                        lock (mapitem.CreaturesBases)
                        {
                            foreach (CreatureBase creatureBase in mapitem.CreaturesBases)
                            {
                                if (creatureBase is Character)
                                {
                                    Character character = (Character)creatureBase;
                                    CharsToUpdate.Add(character);
                                }
                            }
                        }
                    }
                    if ((mapitem.MapX - MapX >= -1 && mapitem.MapX - MapX <= 1) && (mapitem.MapY - MapY >= -1 && mapitem.MapY - MapY <= 1))
                    {
                        lock (mapitem.CreaturesBases)
                        {
                            foreach (CreatureBase creatureBase in mapitem.CreaturesBases)
                            {
                                if (creatureBase is Character)
                                {
                                    Character character = (Character)creatureBase;
                                    CharsToUpdate_Loot.Add(character);
                                }
                            }
                        }
                    }
                }
                foreach (Character character in CharsToUpdate)
                {
                    character.AddMapItem(MapItemToUpdate);
                }
                foreach (Character character in CharsToUpdate_Loot)
                {
                    character.AddFieldLoot(MapItemLoot);
                }

            }//End Lock

        }

        public void GetLootInArea(Character character)
        {
            List<FieldLootItems> MyLoot = new List<FieldLootItems>();

            lock (MapItemsLoot)
            {
                foreach (KeyValuePair<string, FieldLootItems> MapItemLoot in MapItemsLoot)
                {
                    if (MapItemLoot.Value.MapX >= character.MapX - 1 && MapItemLoot.Value.MapX <= character.MapX + 1 &&
                        MapItemLoot.Value.MapY >= character.MapY - 1 && MapItemLoot.Value.MapY <= character.MapY + 1)
                    {
                        MyLoot.Add(MapItemLoot.Value);
                    }
                }
            }
            character.AddFieldLoot(MyLoot, true);
        }

        public Corpse TakeFirstCreatureCorpse(int MapX, int MapY)
        {
            //Lock the loot for this map
            lock (MapItemsLoot)
            {
                FieldLootItems MapItemLoot;
                if (!(MapItemsLoot.TryGetValue(MapX.ToString() + "," + MapY.ToString(), out MapItemLoot)))
                    return null;

                foreach (Loot loot in MapItemLoot.LootItems)
                {
                    if (loot is Corpse)
                    {
                        return (Corpse)TakeItem(MapX, MapY, loot.GameUID);
                    }
                }
                return null;
            }
        }

        public Loot GetItem(int MapX, int MapY, long ItemGUID)
        {
            //Lock the loot for this map
            lock (MapItemsLoot)
            {
                FieldLootItems MapItemLoot;
                if (!(MapItemsLoot.TryGetValue(MapX.ToString() + "," + MapY.ToString(), out MapItemLoot)))
                    return null;

                foreach (Loot loot in MapItemLoot.LootItems)
                {
                    if (loot.GameUID == ItemGUID)
                    {
                        return loot;
                    }
                }
                return null;
            }
        }
    }


    class PendingGroundSpawn
    {
        private Loot groundSpawnToDrop;
        private Timer dropItemTimer;
        private LootManager _lootManager;
        private int dropMapX;
        private int dropMapY;

        public PendingGroundSpawn(LootManager lootManager, Loot groundSpawnItem, int respawnMinutes, int MapX, int MapY)
        {
            _lootManager = lootManager;

            dropItemTimer = new Timer();
            dropItemTimer.AutoReset = false;
            dropItemTimer.Elapsed += new ElapsedEventHandler(dropItemTimer_Elapsed);

            groundSpawnToDrop = groundSpawnItem;
            groundSpawnToDrop.isGroundSpawn = true;
            groundSpawnToDrop.respawnMinutes = respawnMinutes;

            dropMapX = MapX;
            dropMapY = MapY;

            if (respawnMinutes > 0)
                dropItemTimer.Interval = 1000 * 60 * respawnMinutes;
            else
                dropItemTimer.Interval = 3000;

            dropItemTimer.Start();
        }

        void dropItemTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            _lootManager.DropItems(dropMapX, dropMapY, groundSpawnToDrop, false);
        }
    }
}
